'use strict'

const webpack = require('webpack')
const paths = require('./paths')
const isWsl = require('is-wsl') // 检查进程是否是运行在Windows Subsystem for Linux(windows里的bash)
const TerserPlugin = require('terser-webpack-plugin') // 这个插件使用terser来压缩js代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 专门从js组件中抽离css，还支持按需加载和SourceMaps
const postcssNormalize = require('postcss-normalize') // 可以使用一些normalize.css 或者 sanitize.css
const HtmlWebpackPlugin = require('html-webpack-plugin') // 处理html文件，将bundle注入，在具有哈希内容的文件名bundle时特别有用
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 清除所有输入目录文件

module.exports = function(webpackEnv) {
	const isDev = webpackEnv === 'development'
	const isPro = webpackEnv === 'production'

	const cssRegex = /\.css$/
	const cssModuleRegex = /\.module\.css$/
	const sassRegex = /\.(scss|sass)$/
	const sassModuleRegex = /\.modue\.(scss|sass)/
	const fontRegex = /\.(eot|ttf|svg|woff)$/
	const imgRegex = /\.(bmp|gif|jpe?g|png)$/

	const getStyleLoaders = (cssOptions, preProcessor) => {
		const loaders = [
			{
				loader: MiniCssExtractPlugin.loader,
				options: {
					hmr: true
				}
			},
			{
				loader: 'css-loader',
				options: cssOptions
			},
			{
				loader: 'postcss-loader',
				options: {
					ident: 'postcss', // 随便定义，用来唯一标示，在require模块的时候webpack需要这个字段来标示。
					plugins: () => [
						require('postcss-flexbugs-fixes'), // 修复flex的bug
						require('postcss-preset-env')({
							// 内置了autoprefixer
							autoprefixer: {
								flexbox: 'no-2009' // 仅为最终和IE 10版本的规范添加前缀
							},
							stage: 3
						}),
						postcssNormalize()
					],
					sourceMap: isPro
				}
			}
		].filter(Boolean)
		if (preProcessor) {
			loaders.push({
				loader: preProcessor,
				options: {
					sourceMap: isPro
				}
			})
		}
		return loaders
	}

	return {
		mode: isDev ? 'development' : isPro && 'production',
		bail: isPro, // 如果生产环境下，打包出现错误，那么直接终止打包，抛出错误
		devtool: 'cheap-module-eval-source-map', // 添加代码映射
		entry: paths.getAbsPathRelatedToAppDir('src/index.js'),
		output: {
			path: paths.getAbsPathRelatedToAppDir('build'),
			pathinfo: isDev, // 添加 /* filename */ 注释在模块生成的文件中
			filename: isPro
				? 'static/js/[name].[contenthash:8].js'
				: isDev && 'static/js/bundle.js'
			// futureEmitAssets: true,
			// chunkFilename: isPro ? 'static/js/[name].[contenthash:8].chunk.js' : isDev && 'static/js/[name].chunk.js', // 代码分割文件名
			// publicPath: paths.publicUrl // 上线的公开Url
		},
		optimization: {
			// 压缩代码
			minimize: isPro,
			minimizer: [
				new TerserPlugin({
					terserOptions: {
						parse: {
							ecma: 8 // ES8
						},
						compress: {
							ecma: 5, // ES5
							warnings: false, // 压缩不用显示警告
							inline: 2 // 带参数的内联函数
						},
						mangle: {
							safari10: true // 传递true解决Safari 10循环迭代器错误 “无法声明两次let变量”。
						},
						output: {
							ecma: 5, // 输入模式
							comments: false, // 不保留注释
							ascii_only: true // 排除字符串和正则表达式中Unicode字符。
						}
					},
					parallel: !isWsl, // 使用多线程并行构建，提高构建速度。
					cache: true, // 允许代码缓存，默认缓存路径为node_modules/.cache/terser-webpack-plugin
					sourceMap: true
				})
			]
		},
		resolve: {
			modules: ['node_modules'],
			extensions: ['.js', '.json'],
			alias: {
				'@': paths.appSrc
			}
		},
		module: {
			rules: [
				{
					test: /\.js$/,
					enforce: 'pre', // 所有的loader按照前置，行内，普通，后置的顺序执行，要避免其他loader的干扰
					loader: 'eslint-loader',
					options: {
						formatter: require('eslint/lib/cli-engine/formatters/stylish')
					},
					include: paths.appSrc
				},
				{
					test: imgRegex,
					include: paths.appSrc,
					exclude: `${paths.appSrc}/resource/target.jpeg`,
					loader: 'url-loader', // 像file loader一样工作，但是如果文件小于限制，可以返回dataURL
					options: {
						limit: 120000,
						fallback: 'file-loader',
						name: 'static/images/[name].[hash:8].[ext]'
					}
				},
				{
					test: /\.js$/,
					exclude: /node_modules/,
					loader: 'babel-loader',
					options: {
						presets: [
							[
								'@babel/preset-env',
								{
									targets: {
										chrome: '67'
									},
									useBuiltIns: 'usage',
									corejs: 2
								}
							]
						],
						plugins: ['@babel/plugin-syntax-dynamic-import']
					}
				},
				{
					test: fontRegex,
					use: {
						loader: 'url-loader',
						options: {
							name: 'static/font/[name].[hash:8].[ext]',
							limit: 120000
						}
					}
				},
				{
					test: cssRegex,
					exclude: cssModuleRegex,
					use: getStyleLoaders({
						importLoaders: 1, // 在css-loader之前应用的loader数量
						sourceMap: isPro
						// modules: true  不能打开模块化
					}),
					sideEffects: true
				},
				{
					test: cssModuleRegex,
					use: getStyleLoaders({
						importLoaders: 1,
						sourceMap: isPro,
						modules: true
					})
				},
				{
					test: sassRegex,
					exclude: sassModuleRegex,
					use: getStyleLoaders(
						{
							importLoaders: 2,
							sourceMap: isPro
						},
						'sass-loader'
					),
					sideEffects: true
				},
				{
					test: sassModuleRegex,
					use: getStyleLoaders(
						{
							importLoaders: 2,
							sourceMap: isPro,
							modules: true
						},
						'sass-loader'
					)
				},
				{
					loader: 'file-loader',
					exclude: [
						/\.(js|mjs|jsx|ts|tsx|scss|less|css|sass)$/,
						/\.html$/,
						/\.json$/,
						fontRegex,
						imgRegex
					],
					options: {
						name: 'static/media/[name].[hash:8].[ext]'
					}
				},
				{
					test: /\.html$/,
					use: {
						loader: 'html-loader',
						options: {
							attrs: ['img:src', ':data-src', 'audio:src']
						}
					}
				}
			]
		},
		plugins: [
			new CleanWebpackPlugin(),
			new HtmlWebpackPlugin({
				template: `${paths.appPath}/src/index.html`,
				inject: true
			}),
			new MiniCssExtractPlugin({ filename: 'static/media/main.css' }),
			new webpack.optimize.SplitChunksPlugin()
		].filter(Boolean)
	}
}
